﻿// Copyright (c) .NET Foundation and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Roslynator.CSharp.Syntax;

namespace Roslynator.CSharp.Refactorings.ExtractCondition;

internal static class ExtractConditionRefactoring
{
    internal static void ComputeRefactoring(RefactoringContext context, ExpressionChain expressionChain)
    {
        BinaryExpressionSyntax binaryExpression = expressionChain.BinaryExpression;

        SyntaxKind kind = binaryExpression.Kind();

        if (!kind.Is(SyntaxKind.LogicalAndExpression, SyntaxKind.LogicalOrExpression))
            return;

        BinaryExpressionSyntax condition = GetCondition(binaryExpression);

        if (condition is null)
            return;

        SyntaxNode parent = condition.Parent;

        switch (parent?.Kind())
        {
            case SyntaxKind.IfStatement:
            {
                if (kind == SyntaxKind.LogicalAndExpression)
                {
                    ExtractConditionFromIfToNestedIfRefactoring refactoring = ExtractConditionFromIfToNestedIfRefactoring.Instance;

                    context.RegisterRefactoring(
                        refactoring.Title,
                        ct => refactoring.RefactorAsync(context.Document, (IfStatementSyntax)parent, condition, expressionChain, ct),
                        RefactoringDescriptors.ExtractExpressionFromCondition);
                }
                else if (kind == SyntaxKind.LogicalOrExpression)
                {
                    StatementListInfo statementsInfo = SyntaxInfo.StatementListInfo((StatementSyntax)parent);

                    if (statementsInfo.Success)
                    {
                        ExtractConditionFromIfToIfRefactoring refactoring = ExtractConditionFromIfToIfRefactoring.Instance;

                        context.RegisterRefactoring(
                            refactoring.Title,
                            ct => refactoring.RefactorAsync(context.Document, statementsInfo, condition, expressionChain, ct),
                            RefactoringDescriptors.ExtractExpressionFromCondition);
                    }
                }

                break;
            }
            case SyntaxKind.WhileStatement:
            {
                if (kind == SyntaxKind.LogicalAndExpression)
                {
                    ExtractConditionFromWhileToNestedIfRefactoring refactoring = ExtractConditionFromWhileToNestedIfRefactoring.Instance;

                    context.RegisterRefactoring(
                        refactoring.Title,
                        ct => refactoring.RefactorAsync(context.Document, (WhileStatementSyntax)parent, condition, expressionChain, ct),
                        RefactoringDescriptors.ExtractExpressionFromCondition);
                }

                break;
            }
        }
    }

    public static void ComputeRefactoring(RefactoringContext context, ExpressionSyntax expression)
    {
        SyntaxNode parent = expression.Parent;

        if (parent is null)
            return;

        SyntaxKind kind = parent.Kind();

        if (!kind.Is(SyntaxKind.LogicalAndExpression, SyntaxKind.LogicalOrExpression))
            return;

        BinaryExpressionSyntax binaryExpression = GetCondition((BinaryExpressionSyntax)parent);

        if (binaryExpression is null)
            return;

        parent = binaryExpression.Parent;

        switch (parent?.Kind())
        {
            case SyntaxKind.IfStatement:
            {
                if (kind == SyntaxKind.LogicalAndExpression)
                {
                    ExtractConditionFromIfToNestedIfRefactoring refactoring = ExtractConditionFromIfToNestedIfRefactoring.Instance;

                    context.RegisterRefactoring(
                        refactoring.Title,
                        ct => refactoring.RefactorAsync(context.Document, binaryExpression, expression, ct),
                        RefactoringDescriptors.ExtractExpressionFromCondition);
                }
                else if (kind == SyntaxKind.LogicalOrExpression)
                {
                    StatementListInfo statementsInfo = SyntaxInfo.StatementListInfo((StatementSyntax)parent);

                    if (statementsInfo.Success)
                    {
                        ExtractConditionFromIfToIfRefactoring refactoring = ExtractConditionFromIfToIfRefactoring.Instance;

                        context.RegisterRefactoring(
                            refactoring.Title,
                            ct => refactoring.RefactorAsync(context.Document, statementsInfo, binaryExpression, expression, ct),
                            RefactoringDescriptors.ExtractExpressionFromCondition);
                    }
                }

                break;
            }
            case SyntaxKind.WhileStatement:
            {
                if (kind == SyntaxKind.LogicalAndExpression)
                {
                    StatementListInfo statementsInfo = SyntaxInfo.StatementListInfo((StatementSyntax)parent);

                    if (statementsInfo.Success)
                    {
                        ExtractConditionFromWhileToNestedIfRefactoring refactoring = ExtractConditionFromWhileToNestedIfRefactoring.Instance;

                        context.RegisterRefactoring(
                            refactoring.Title,
                            ct => refactoring.RefactorAsync(context.Document, (WhileStatementSyntax)parent, binaryExpression, expression, ct),
                            RefactoringDescriptors.ExtractExpressionFromCondition);
                    }
                }

                break;
            }
        }
    }

    private static BinaryExpressionSyntax GetCondition(BinaryExpressionSyntax binaryExpression)
    {
        SyntaxKind kind = binaryExpression.Kind();

        for (SyntaxNode parent = binaryExpression.Parent; parent is not null; parent = parent.Parent)
        {
            SyntaxKind parentKind = parent.Kind();

            if (parentKind == kind)
            {
                binaryExpression = (BinaryExpressionSyntax)parent;
            }
            else if (parentKind == SyntaxKind.IfStatement || parentKind == SyntaxKind.WhileStatement)
            {
                return binaryExpression;
            }
            else
            {
                return null;
            }
        }

        return null;
    }
}
